/************************************************************************************

Filename    :   OVR_Delegates.h
Content     :   C++ Delegates
Created     :   June 15, 2014
Authors     :   Chris Taylor

Copyright   :   Copyright 2014-2016 Oculus VR, LLC All Rights reserved.

Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.

You may obtain a copy of the License at

http://www.oculusvr.com/licenses/LICENSE-3.3

Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

************************************************************************************/

/*
    Based on The Impossibly Fast C++ Delegates by Sergey Ryazanov from
    http://www.codeproject.com/KB/cpp/ImpossiblyFastCppDelegate.aspx (2005)
*/

/*
    Usage:

    Declare a delegate with a void (int) signature, also known as a
    function that returns void and has one parameter that is an int:
        typedef Delegate1<void, int> MyDelegate;
        MyDelegate d;

    Point the delegate to a member function:
        d.SetMember<A, &A::TestFunctionA>(&a);
        d = MyDelegate::FromMember<A, &A::TestFunctionA>(&a);

    Point the delegate to a const member function:
        d.SetConstMember<C, &C::TestFunctionA>(&c);
        d = MyDelegate::FromConstMember<C, &C::TestFunctionA>(&c);

    Point the delegate to a free function:
        d.SetFree<&FreeFunctionX>();
        d = MyDelegate::FromFree<&FreeFunctionX>();

    Invoke the function via the delegate (works for all 3 cases):
        d(1000);

    By default the delegates are uninitialized.
    To clear an array of delegates quickly just zero the memory.

    This implementation is nicer than FastDelegates in my opinion
    because it is simple and easy to read.  It is a little slower
    for virtual functions, but the size of the delegate is small,
    and it will only get better as compilers improve.
*/

#ifndef OVR_Delegates_h
#define OVR_Delegates_h

#include "OVR_Types.h"

namespace OVR {

template <class ret_type>
class Delegate0 {
  typedef ret_type (*StubPointer)(void*);
  typedef Delegate0<ret_type> this_type;

  void* _object;
  StubPointer _stub;

  inline Delegate0(void* object, StubPointer stub) {
    _object = object;
    _stub = stub;
  }

  // Stubs

  template <ret_type (*F)()>
  static inline ret_type FreeStub(void* /*object*/) {
    return (F)();
  }

  template <class T, ret_type (T::*F)()>
  static inline ret_type MemberStub(void* object) {
    T* p = static_cast<T*>(object);
    return (p->*F)();
  }

  template <class T, ret_type (T::*F)() const>
  static inline ret_type ConstMemberStub(void* object) {
    T* p = static_cast<T*>(object);
    return (p->*F)();
  }

 public:
  inline Delegate0() : _object(0), _stub(0) {}

  // Function invocation

  inline ret_type operator()() const {
    return (*_stub)(_object);
  }

  // Use stub pointer as a validity flag and equality checker

  inline bool operator==(const this_type& rhs) const {
    return _object == rhs._object && _stub == rhs._stub;
  }

  inline bool operator!=(const this_type& rhs) const {
    return _object != rhs._object || _stub != rhs._stub;
  }

  inline bool IsValid() const {
    return _stub != 0;
  }

  inline bool operator!() const {
    return _stub == 0;
  }

  inline void Invalidate() {
    _stub = 0;
  }

  // Delegate creation from a function

  template <ret_type (*F)()>
  static inline this_type FromFree() {
    return this_type(0, &FreeStub<F>);
  }

  template <class T, ret_type (T::*F)()>
  static inline this_type FromMember(T* object) {
    return this_type(object, &MemberStub<T, F>);
  }

  template <class T, ret_type (T::*F)() const>
  static inline this_type FromConstMember(T const* object) {
    return this_type(const_cast<T*>(object), &ConstMemberStub<T, F>);
  }

  // In-place assignment to a different function

  template <ret_type (*F)()>
  inline void SetFree() {
    *this = FromFree<F>();
  }

  template <class T, ret_type (T::*F)()>
  inline void SetMember(T* object) {
    *this = FromMember<T, F>(object);
  }

  template <class T, ret_type (T::*F)() const>
  inline void SetConstMember(T const* object) {
    *this = FromConstMember<T, F>(object);
  }
};

template <class ret_type, class arg1_type>
class Delegate1 {
  typedef ret_type (*StubPointer)(void*, arg1_type);
  typedef Delegate1<ret_type, arg1_type> this_type;

  void* _object;
  StubPointer _stub;

  inline Delegate1(void* object, StubPointer stub) {
    _object = object;
    _stub = stub;
  }

  // Stubs

  template <ret_type (*F)(arg1_type)>
  static inline ret_type FreeStub(void* /*object*/, arg1_type a1) {
    return (F)(a1);
  }

  template <class T, ret_type (T::*F)(arg1_type)>
  static inline ret_type MemberStub(void* object, arg1_type a1) {
    T* p = static_cast<T*>(object);
    return (p->*F)(a1);
  }

  template <class T, ret_type (T::*F)(arg1_type) const>
  static inline ret_type ConstMemberStub(void* object, arg1_type a1) {
    T* p = static_cast<T*>(object);
    return (p->*F)(a1);
  }

 public:
  inline Delegate1() : _object(0), _stub(0) {}

  // Function invocation

  inline ret_type operator()(arg1_type a1) const {
    return (*_stub)(_object, a1);
  }

  // Use stub pointer as a validity flag and equality checker

  inline bool operator==(const this_type& rhs) const {
    return _object == rhs._object && _stub == rhs._stub;
  }

  inline bool operator!=(const this_type& rhs) const {
    return _object != rhs._object || _stub != rhs._stub;
  }

  inline bool IsValid() const {
    return _stub != 0;
  }

  inline bool operator!() const {
    return _stub == 0;
  }

  inline void Invalidate() {
    _stub = 0;
  }

  // Delegate creation from a function

  template <ret_type (*F)(arg1_type)>
  static inline this_type FromFree() {
    return this_type(0, &FreeStub<F>);
  }

  template <class T, ret_type (T::*F)(arg1_type)>
  static inline this_type FromMember(T* object) {
    return this_type(object, &MemberStub<T, F>);
  }

  template <class T, ret_type (T::*F)(arg1_type) const>
  static inline this_type FromConstMember(T const* object) {
    return this_type(const_cast<T*>(object), &ConstMemberStub<T, F>);
  }

  // In-place assignment to a different function

  template <ret_type (*F)(arg1_type)>
  inline void SetFree() {
    *this = FromFree<F>();
  }

  template <class T, ret_type (T::*F)(arg1_type)>
  inline void SetMember(T* object) {
    *this = FromMember<T, F>(object);
  }

  template <class T, ret_type (T::*F)(arg1_type) const>
  inline void SetConstMember(T const* object) {
    *this = FromConstMember<T, F>(object);
  }
};

template <class ret_type, class arg1_type, class arg2_type>
class Delegate2 {
  typedef ret_type (*StubPointer)(void*, arg1_type, arg2_type);
  typedef Delegate2<ret_type, arg1_type, arg2_type> this_type;

  void* _object;
  StubPointer _stub;

  inline Delegate2(void* object, StubPointer stub) {
    _object = object;
    _stub = stub;
  }

  // Stubs

  template <ret_type (*F)(arg1_type, arg2_type)>
  static inline ret_type FreeStub(void* /*object*/, arg1_type a1, arg2_type a2) {
    return (F)(a1, a2);
  }

  template <class T, ret_type (T::*F)(arg1_type, arg2_type)>
  static inline ret_type MemberStub(void* object, arg1_type a1, arg2_type a2) {
    T* p = static_cast<T*>(object);
    return (p->*F)(a1, a2);
  }

  template <class T, ret_type (T::*F)(arg1_type, arg2_type) const>
  static inline ret_type ConstMemberStub(void* object, arg1_type a1, arg2_type a2) {
    T* p = static_cast<T*>(object);
    return (p->*F)(a1, a2);
  }

 public:
  inline Delegate2() : _object(0), _stub(0) {}

  // Function invocation

  inline ret_type operator()(arg1_type a1, arg2_type a2) const {
    return (*_stub)(_object, a1, a2);
  }

  // Use stub pointer as a validity flag and equality checker

  inline bool operator==(const this_type& rhs) const {
    return _object == rhs._object && _stub == rhs._stub;
  }

  inline bool operator!=(const this_type& rhs) const {
    return _object != rhs._object || _stub != rhs._stub;
  }

  inline bool IsValid() const {
    return _stub != 0;
  }

  inline bool operator!() const {
    return _stub == 0;
  }

  inline void Invalidate() {
    _stub = 0;
  }

  // Delegate creation from a function

  template <ret_type (*F)(arg1_type, arg2_type)>
  static inline this_type FromFree() {
    return this_type(0, &FreeStub<F>);
  }

  template <class T, ret_type (T::*F)(arg1_type, arg2_type)>
  static inline this_type FromMember(T* object) {
    return this_type(object, &MemberStub<T, F>);
  }

  template <class T, ret_type (T::*F)(arg1_type, arg2_type) const>
  static inline this_type FromConstMember(T const* object) {
    return this_type(const_cast<T*>(object), &ConstMemberStub<T, F>);
  }

  // In-place assignment to a different function

  template <ret_type (*F)(arg1_type, arg2_type)>
  inline void SetFree() {
    *this = FromFree<F>();
  }

  template <class T, ret_type (T::*F)(arg1_type, arg2_type)>
  inline void SetMember(T* object) {
    *this = FromMember<T, F>(object);
  }

  template <class T, ret_type (T::*F)(arg1_type, arg2_type) const>
  inline void SetConstMember(T const* object) {
    *this = FromConstMember<T, F>(object);
  }
};

template <class ret_type, class arg1_type, class arg2_type, class arg3_type>
class Delegate3 {
  typedef ret_type (*StubPointer)(void*, arg1_type, arg2_type, arg3_type);
  typedef Delegate3<ret_type, arg1_type, arg2_type, arg3_type> this_type;

  void* _object;
  StubPointer _stub;

  inline Delegate3(void* object, StubPointer stub) {
    _object = object;
    _stub = stub;
  }

  // Stubs

  template <ret_type (*F)(arg1_type, arg2_type, arg3_type)>
  static inline ret_type FreeStub(void* /*object*/, arg1_type a1, arg2_type a2, arg3_type a3) {
    return (F)(a1, a2, a3);
  }

  template <class T, ret_type (T::*F)(arg1_type, arg2_type, arg3_type)>
  static inline ret_type MemberStub(void* object, arg1_type a1, arg2_type a2, arg3_type a3) {
    T* p = static_cast<T*>(object);
    return (p->*F)(a1, a2, a3);
  }

  template <class T, ret_type (T::*F)(arg1_type, arg2_type, arg3_type) const>
  static inline ret_type ConstMemberStub(void* object, arg1_type a1, arg2_type a2, arg3_type a3) {
    T* p = static_cast<T*>(object);
    return (p->*F)(a1, a2, a3);
  }

 public:
  inline Delegate3() : _object(0), _stub(0) {}

  // Function invocation

  inline ret_type operator()(arg1_type a1, arg2_type a2, arg3_type a3) const {
    return (*_stub)(_object, a1, a2, a3);
  }

  // Use stub pointer as a validity flag and equality checker

  inline bool operator==(const this_type& rhs) const {
    return _object == rhs._object && _stub == rhs._stub;
  }

  inline bool operator!=(const this_type& rhs) const {
    return _object != rhs._object || _stub != rhs._stub;
  }

  inline bool IsValid() const {
    return _stub != 0;
  }

  inline bool operator!() const {
    return _stub == 0;
  }

  inline void Invalidate() {
    _stub = 0;
  }

  // Delegate creation from a function

  template <ret_type (*F)(arg1_type, arg2_type, arg3_type)>
  static inline this_type FromFree() {
    return this_type(0, &FreeStub<F>);
  }

  template <class T, ret_type (T::*F)(arg1_type, arg2_type, arg3_type)>
  static inline this_type FromMember(T* object) {
    return this_type(object, &MemberStub<T, F>);
  }

  template <class T, ret_type (T::*F)(arg1_type, arg2_type, arg3_type) const>
  static inline this_type FromConstMember(T const* object) {
    return this_type(const_cast<T*>(object), &ConstMemberStub<T, F>);
  }

  // In-place assignment to a different function

  template <ret_type (*F)(arg1_type, arg2_type, arg3_type)>
  inline void SetFree() {
    *this = FromFree<F>();
  }

  template <class T, ret_type (T::*F)(arg1_type, arg2_type, arg3_type)>
  inline void SetMember(T* object) {
    *this = FromMember<T, F>(object);
  }

  template <class T, ret_type (T::*F)(arg1_type, arg2_type, arg3_type) const>
  inline void SetConstMember(T const* object) {
    *this = FromConstMember<T, F>(object);
  }
};

// Add more here if needed, but keep in mind that a short, simple interface
// is rewarded by making the delegates faster...

} // namespace OVR

#endif // OVR_Delegates_h
